/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package edu.usc.ee.java;

import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.graph.UndirectedGraph;
import edu.uci.ics.jung.graph.UndirectedSparseGraph;
import edu.uci.ics.jung.visualization.BasicVisualizationServer;
import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Paint;
import java.awt.Stroke;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import javax.swing.JFrame;
import org.apache.commons.collections15.Transformer;

/**
 *
 * @author zhouzhao
 * @version 1.0
 */
public class switchboxRouter {

    private Integer R; //R = row
    private Integer C; //C = column
    private Integer Template;
    private Integer Xg;
    private Integer Yg;

    private UndirectedGraph<via, wire> box;
    private via[][] v;
    private wire[][] w;

    private Integer[] north;
    private Integer[] south;
    private Integer[] east;
    private Integer[] west;

    private Map<Integer, Set<via>> netlist;
    private List<via> vpath;
    private List<wire> epath;
    private Map<Integer, List<via>> vroute;
    private Map<Integer, List<wire>> eroute;

    private double temperature = 10;
    

    public switchboxRouter(Integer row, Integer col, Integer template, Integer x, Integer y,
            Integer north[], Integer south[], Integer east[], Integer west[]){

        this.R = row;
        this.C = col;
        this.v = new via[C][R];
        this.w = new wire[C*2-1][R*2-1];

        this.Template = template;
        this.Xg = x;
        this.Yg = y;

        this.north = north;
        this.south = south;
        this.east = east;
        this.west = west;
      
        this.netlist = new HashMap<Integer, Set<via>>();
        this.vroute = new HashMap<Integer, List<via>>();
        this.eroute = new HashMap<Integer, List<wire>>();
        
    }

    public Integer getRow(){
        return this.R;
    }

    public Integer getCol(){
        return this.C;
    }

    public UndirectedGraph<via, wire> getBox(){
        return this.box;
    }

    public Map<Integer, Set<via>> getNetlist(){
        return this.netlist;
    }

    public via getVia(int x, int y){
        return this.v[x][y];
    }

    public wire getWire(int x, int y){
        return this.w[x][y];
    }

    public void graphConstruct()
    {
        box = new UndirectedSparseGraph<via, wire>();

        for(int i=0; i<R; i++){
            for(int j=0; j<C; j++){
                v[j][i] = new via(C*i+j, j, i);
            }
        }

        for(int i=0; i<R*2-1; i=i+2){
            for(int j=1; j<C*2-1; j=j+2){
                w[j][i] = new wire((C*2-1)*i+j, j, i);
                box.addEdge(w[j][i], v[(j-1)/2][i/2], v[(j+1)/2][i/2]);
            }
        }

        for(int j=0; j<C*2-1; j=j+2){
            for(int i=1; i<R*2-1; i=i+2){
                w[j][i] = new wire((C*2-1)*i+j, j, i);
//                System.out.println("j="+j+" i="+i);
                box.addEdge(w[j][i], v[j/2][(i-1)/2], v[j/2][(i+1)/2]);
            }
        }

    }

    public void northSearch(Integer net){

        Set<via> vset;
        Set<Integer> signal;
        
        for(int j=0; j<C; j++){
            if(net==north[j]){
                signal=netlist.keySet();
                if(signal.contains(net)){
                    netlist.get(net).add(v[j][0]);
                }else{
                    vset = new HashSet<via>();
                    vset.add(v[j][0]);
                    netlist.put(net, vset);
                }
            }
        }
    }

    public void southSearch(Integer net){

        Set<via> vset;
        Set<Integer> signal;

        for(int j=0; j<C; j++){
            if(net==south[j]){
                signal = netlist.keySet();
                if(signal.contains(net)){
                    netlist.get(net).add(v[j][R-1]);
                }else{
                    vset = new HashSet<via>();
                    vset.add(v[j][R-1]);
                    netlist.put(net, vset);
                }
            }
        }
    }

    public void eastSearch(Integer net){

        Set<via> vset;
        Set<Integer> signal;
        
        for(int j=0; j<R; j++){
            if(net==east[j]){
                signal = netlist.keySet();
                if(signal.contains(net)){
                    netlist.get(net).add(v[C-1][j]);
                }else{
                    vset = new HashSet<via>();
                    vset.add(v[C-1][j]);
                    netlist.put(net, vset);
                }
            }
        }
    }

    public void westSearch(Integer net){

        Set<via> vset;
        Set<Integer> signal;
        
        for(int j=0; j<R; j++){
            if(net==west[j]){
                signal = netlist.keySet();
                if(signal.contains(net)){
                    netlist.get(net).add(v[0][j]);
                }else{
                    vset = new HashSet<via>();
                    vset.add(v[0][j]);
                    netlist.put(net, vset);
                }
            }
        }
    }
    

    public void netlistConstruct(){

        for(int j=0; j<C; j++){
            if(north[j]!=0){
                northSearch(north[j]);
                southSearch(north[j]);
                eastSearch(north[j]);
                westSearch(north[j]);
            }
            if(south[j]!=0){
                northSearch(south[j]);
                southSearch(south[j]);
                eastSearch(south[j]);
                westSearch(south[j]);
            }
        }

        for(int i=0; i<R; i++){
            if(east[i]!=0){
                northSearch(east[i]);
                southSearch(east[i]);
                eastSearch(east[i]);
                westSearch(east[i]);
            }
            if(west[i]!=0){
                northSearch(west[i]);
                southSearch(west[i]);
                eastSearch(west[i]);
                westSearch(west[i]);
            }   
        }

        System.out.println(netlist);
    }

    public void congest(Set<via> vset)
    {
        int xmin, xmax, ymin, ymax;
        int weight;
        via[] a = vset.toArray(new via[0]);

        xmin = a[0].getx();
        xmax = xmin;
        ymin = a[0].gety();
        ymax = ymin;

        for(int i=1; i<a.length; i++){
            xmin = a[i].getx()<xmin? a[i].getx():xmin;
            xmax = a[i].getx()>xmax? a[i].getx():xmax;
            ymin = a[i].gety()<ymin? a[i].gety():ymin;
            ymax = a[i].gety()>ymax? a[i].gety():ymax;
        }
        
//        System.out.println("xmin:"+xmin+" xmax:"+xmax+" ymin:"+ymin+" ymax:"+ymax);
        for(int row = ymin; row <= ymax; row++){
            for(int column = xmin; column <= xmax; column++)
            {
                    weight = v[column][row].getWeight();
                    v[column][row].setWeight(++weight);
//                    v[column][row].setColor(myColor.YELLOW);
            }
        }
    }

    public void congestRoute(){

        Set<Integer> signal = vroute.keySet();

        for(Integer i: signal){
            rightAngle(i, 1);
            for(via j: vroute.get(i)){
                j.setWeight(j.getWeight()+1);
            }
        }
    }

    public void ripup(Integer net){

        System.out.println("The signal "+net+" is ripup");
        
        if(vroute.containsKey(net)){
            rightAngle(net, -1);
            for(via j: vroute.get(net)){
                j.setWeight(j.getWeight()-1);
            }
        }else{
            System.out.println("The signal "+net+" is not routed in vroute");
        }

        if(eroute.containsKey(net)){
            for(wire k: eroute.get(net)){
                k.setCapacity(k.getCapacity()-1);
            }
        }else{
            System.out.println("The signal "+net+" is not routed in eroute");
        }

    }


    /**
     * 
     * @param parent
     * @return
     */
    public Map<via, Set<via>> findChildren(via parent){

        Set<via> children = new HashSet<via>();
        Map<via, Set<via>> group = new HashMap<via, Set<via>>();
        int x = parent.getx();
        int y = parent.gety();

        if(y==0){
            if(x==0){
                children.add(v[x+1][y]);
                children.add(v[x][y+1]);
            }else{
                if(x==C-1){
                    children.add(v[x-1][y]);
                    children.add(v[x][y+1]);
                }else{
                    children.add(v[x-1][y]);
                    children.add(v[x+1][y]);
                    children.add(v[x][y+1]);
                }
            }

        }else{
            if(y==R-1){
                if(x==0){
                    children.add(v[x+1][y]);
                    children.add(v[x][y-1]);
                }else{
                    if(x==C-1){
                        children.add(v[x-1][y]);
                        children.add(v[x][y-1]);
                    }else{
                        children.add(v[x-1][y]);
                        children.add(v[x+1][y]);
                        children.add(v[x][y-1]);
                    }
                }
            }else{
                    if(x==0){
                        children.add(v[x][y-1]);
                        children.add(v[x][y+1]);
                        children.add(v[x+1][y]);
                    }else{
                        if(x==C-1){
                            children.add(v[x][y-1]);
                            children.add(v[x][y+1]);
                            children.add(v[x-1][y]);
                        }else{
                            children.add(v[x-1][y]);
                            children.add(v[x+1][y]);
                            children.add(v[x][y-1]);
                            children.add(v[x][y+1]);
                        }
                    }
            }

        }

        group.put(parent, children);
        return group;
    }

    public Map<via, Set<via>> direction1(via parent, via child){
        //filter some elements from set
        Map<via, Set<via>> grandchild = new HashMap<via, Set<via>>();
        Map<via, Set<via>> grand = new HashMap<via, Set<via>>();

        grandchild = findChildren(child);
        grand = findChildren(child);

        Set<via> key = grandchild.keySet();

        for(via i: key){
                if(!grand.get(i).remove(parent)){
                    System.out.println("invalid parent or child");
                }
                for(via j: grandchild.get(i)){
                    if(j.getUpdate()){
                        grand.get(i).remove(j);
                    }
                }
        }

        return grand;
    }

    public Map<via, Set<via>> direction2(via parent, via child){
        //filter some elements from set
        Map<via, Set<via>> grandchild = new HashMap<via, Set<via>>();
        Map<via, Set<via>> grand = new HashMap<via, Set<via>>();

        grandchild = findChildren(child);
        grand = findChildren(child);

        Set<via> key = grandchild.keySet();

        for(via i: key){
                if(!grand.get(i).remove(parent)){
                    System.out.println("invalid parent or child");
                }
        }

        return grand;
    }
     
    public void waveCompute(via source, via target)
    {
        Map<via, Set<via>> group = new HashMap<via, Set<via>>();
        Queue<List<via>> q = new LinkedList<List<via>>();
        source.setWeightWave(source.getWeight());

        group = findChildren(source);
        Set<via> key = group.keySet();
        weightCompute(group);

        for(via i: key){
            for(via j: group.get(i)){
                List<via> pair = new ArrayList<via>();
                pair.add(0, i);
                pair.add(1, j);
                q.add(pair);
            }
        }

        while(!q.isEmpty()){

        List<via> head = q.remove();
        via key1 = head.get(0);
        via value1 = head.get(1);

        if(key1.equals(target)){
            q.clear();
        }else{
        group = direction1(key1, value1);
        Set<via> key2 = group.keySet();
        
        for(via i: key2){
            for(via j: group.get(i)){
                List<via> pair = new ArrayList<via>();
                pair.add(0, i);
                pair.add(1, j);
                q.add(pair);
            }
        }
        
        group = direction2(key1, value1);
        weightCompute(group);

        }
        }
    }

    public void weightCompute(Map<via, Set<via>> group){

        Set<via> key = group.keySet();

        for(via i: key){
            for(via j: group.get(i)){
                if(!j.getUpdate()){
                    j.setWeightWave(i.getWeightWave()+j.getWeight()+1);
                    j.setUpdate(true);
                }else{
                    if(i.getWeightWave()+j.getWeight()+1<j.getWeightWave()){
                        j.setWeightWave(i.getWeightWave()+j.getWeight()+1);
                    }
                }
//                System.out.println(i+" ("+i.getWeight()+","+i.getWeightWave()+") "+j+" ("+j.getWeight()+","+j.getWeightWave()+")");
            }
        }
    }

    public void backTrack(via source, via target){

        vpath = new ArrayList<via>();
        epath = new ArrayList<wire>();

        target.setColor(myColor.RED);
        via m = recursive(source, target);
        m.setColor(myColor.RED);
//        System.out.println("the final node is "+m);
        vpath.add(m);
        vpath.add(target);
        epath.add(getWire(m.getx()+target.getx(), m.gety()+target.gety()));

//       System.out.println(vpath);
//        System.out.println(epath);

    }

    public via recursive(via source, via target){

        Map<via, Set<via>> group = new HashMap<via, Set<via>>();
        group = findChildren(target);
        via m = minCompute(group);
//        System.out.println("Source="+source+" Target="+target);
        if(!m.equals(source)){
            via r = recursive(source, m);
            vpath.add(r);
            r.setColor(myColor.RED);
            int x = m.getx()+r.getx();
            int y = m.gety()+r.gety();
            epath.add(getWire(x, y));
            return m;
        }

        return m;
    }

    public via minCompute(Map<via, Set<via>> group){

        Set<via> key = group.keySet();
        via [] a;
        via min = new via();

        for(via i: key){
            Set<via> value = group.get(i);
            a = value.toArray(new via[0]);
            min = a[0];

            for(via j: value){
                min = j.getWeightWave()<min.getWeightWave()? j:min;
            }

            //System.out.println("the min vertex around "+i+" is "+min);
        }

        return min;

    }

    public void updateWire(List<wire> path){

        for(int i=0; i<path.size(); i++){
            wire t = path.get(i);
            t.setCapacity(t.getCapacity()+1);
        }
    }

    public void updateVia(List<via> path){

        for(int i=0; i<path.size(); i++){
            via t = path.get(i);
            t.setWeight(t.getWeight()+1);
        }
    }

    public void waveClear(){
        for(int row = 0; row < R; row++){
            for(int column = 0; column < C; column++)
            {   
                    v[column][row].setWeightWave(0);
                    v[column][row].setUpdate(false);
            }
        }
        
    }

    public void weightClear(){
        for(int row = 0; row < R; row++){
            for(int column = 0; column < C; column++)
            {
                    v[column][row].setWeight(0);
            }
        }
    }


    public void route(Integer net){

        via[] path;

        Map<Integer, List<via>> vcomp = new HashMap<Integer, List<via>>();
        Map<Integer, List<wire>> ecomp = new HashMap<Integer, List<wire>>();
        Set<Integer> length;

        Integer[] len;
        boolean swap = false;
        Integer temp;

        List<via> vbuffer;
        List<wire> ebuffer;

        System.out.println("The signal "+net+" is routed initially");
        if(netlist.containsKey(net)){
            Set<via> value = netlist.get(net);

            if(value.size()==2){
                path = value.toArray(new via[0]);
                waveCompute(path[0], path[1]);
                backTrack(path[0], path[1]);
                vroute.put(net, vpath);
                eroute.put(net, epath);
                updateWire(epath);
                waveClear();
            }else{
                //multi-node route based on MST without Steiner points
                if(value.size()==3){
                    path = value.toArray(new via[0]);
                    waveCompute(path[0], path[1]);
                    backTrack(path[0], path[1]);
                    vcomp.put(1, vpath);
                    ecomp.put(1, epath);
                    waveClear();

                    waveCompute(path[0], path[2]);
                    backTrack(path[0], path[2]);
                    vcomp.put(2, vpath);
                    ecomp.put(2, epath);
                    waveClear();

                    waveCompute(path[1], path[2]);
                    backTrack(path[1], path[2]);
                    vcomp.put(3, vpath);
                    ecomp.put(3, epath);
                    waveClear();

                    length = vcomp.keySet();
                    len = length.toArray(new Integer[0]);
                    while(!swap){
                        swap = true;
                        for(int i=0; i<len.length-2; i++){
                            if(vcomp.get(len[i]).size() > vcomp.get(len[i+1]).size()){
                                temp = len[i];
                                len[i] = len[i+1];
                                len[i+1] = temp;
                                swap = false;
                            }
                        }
                    }

                    vbuffer = new ArrayList<via>();
                    vbuffer.addAll(vcomp.get(len[0]));
                    vbuffer.addAll(vcomp.get(len[1]));
                    vroute.put(net, vbuffer);
                    System.out.println(vbuffer);

                    ebuffer = new ArrayList<wire>();
                    ebuffer.addAll(ecomp.get(len[0]));
                    ebuffer.addAll(ecomp.get(len[1]));
                    eroute.put(net, ebuffer);
                    updateWire(ebuffer);
                }
            }
        }else{
            System.out.println("The signal "+net+" is not routable");
        }
    }


    public void initialRoute(){

        Set<Integer> keyset = getNetlist().keySet();

        for(Integer i: keyset){
            Set<via> value = getNetlist().get(i);
            congest(value);
        }

        for(Integer j: keyset){
            route(j);
        }

        System.out.println(vroute);
        System.out.println(eroute);
    }

    public void reRoute(Integer net){

        via[] path;

        Map<Integer, List<via>> vcomp = new HashMap<Integer, List<via>>();
        Map<Integer, List<wire>> ecomp = new HashMap<Integer, List<wire>>();
        Set<Integer> length;

        Integer[] len;
        boolean swap = false;
        Integer temp;

        List<via> vbuffer = new ArrayList<via>();
        List<wire> ebuffer = new ArrayList<wire>();

        System.out.println("The signal "+net+" is re-routed");
        if(netlist.containsKey(net)){
            Set<via> value = netlist.get(net);

            if(value.size()==2){
                path = value.toArray(new via[0]);
                waveCompute(path[0], path[1]);
                backTrack(path[0], path[1]);
                vroute.put(net, vpath);
                eroute.put(net, epath);
                updateVia(vpath);
                updateWire(epath);
                waveClear();
            }else{
                //multi-point route
                if(value.size()==3){
                    path = value.toArray(new via[0]);
                    waveCompute(path[0], path[1]);
                    backTrack(path[0], path[1]);
                    vcomp.put(1, vpath);
                    ecomp.put(1, epath);
                    waveClear();

                    waveCompute(path[0], path[2]);
                    backTrack(path[0], path[2]);
                    vcomp.put(2, vpath);
                    ecomp.put(2, epath);
                    waveClear();

                    waveCompute(path[1], path[2]);
                    backTrack(path[1], path[2]);
                    vcomp.put(3, vpath);
                    ecomp.put(3, epath);
                    waveClear();

                    length = vcomp.keySet();
                    len = length.toArray(new Integer[0]);
                    while(!swap){
                        swap = true;
                        for(int i=0; i<len.length-2; i++){
                            if(vcomp.get(len[i]).size() > vcomp.get(len[i+1]).size()){
                                temp = len[i];
                                len[i] = len[i+1];
                                len[i+1] = temp;
                                swap = false;
                            }
                        }
                    }

                    vbuffer.addAll(vcomp.get(len[0]));
                    vbuffer.addAll(vcomp.get(len[1]));
                    vroute.put(net, vbuffer);
                    updateVia(vbuffer);
                    System.out.println(vbuffer);
                    
                    ebuffer.addAll(ecomp.get(len[0]));
                    ebuffer.addAll(ecomp.get(len[1]));
                    eroute.put(net, ebuffer);
                    updateWire(ebuffer);
                }
            }
        }else{
            System.out.println("The signal "+net+" is not routable");
        }

        System.out.println(vroute);
//        System.out.println(eroute);
        
    }

    public void viaCompare(via vpn, via vc){
        
        switch (vpn.getx()-vc.getx()){
                    case -1: vc.setWest(1); break;
                    case 0: break;
                    case 1: vc.setEast(1); break;
                    default: System.out.println("invalid x"); break;
                }

        switch (vpn.gety()-vc.gety()){
                    case -1: vc.setNorth(1); break;
                    case 0: break;
                    case 1: vc.setSouth(1); break;
                    default: System.out.println("invalid y"); break;
                }

        if((vc.getNorth()==1 && vc.getEast()==1) ||
                (vc.getNorth()==1 && vc.getWest()==1) ||
                (vc.getSouth()==1 && vc.getEast()==1) ||
                (vc.getSouth()==1 && vc.getWest()==1)){
//                    vc.setCenter(vc.getCenter()+1);
        }
    }

    public void viaSide(via vh){

        int x = vh.getx();
        int y = vh.gety();

        if(y==0){
            vh.setNorth(1);
        }

        if(y==R-1){
            vh.setSouth(1);
        }

        if(x==0){
            vh.setWest(1);
        }

        if(x==C-1){
            vh.setEast(1);
        }
        
    }

    public void viaConfigure(){

        Set<Integer> signal = vroute.keySet();
        int k;

        for(Integer i: signal){
            via[] temp = vroute.get(i).toArray(new via[0]);
            k = temp.length;

            viaCompare(temp[1], temp[0]);
            viaSide(temp[0]);

            for(int j=1; j<k-1; j++){
                viaCompare(temp[j-1], temp[j]);
                viaCompare(temp[j+1], temp[j]);
            }

            viaCompare(temp[k-2], temp[k-1]);
            viaSide(temp[k-1]);
        }

    }

    public void rightAngle(Integer net, Integer delta){

        Set<Integer> signal = vroute.keySet();
        int len;

        if(signal.contains(net)){
            via[] temp = vroute.get(net).toArray(new via[0]);
            len = temp.length;

            for(int j=1; j<len-1; j++){
                if(temp[j].getx()==temp[j-1].getx() && temp[j].getx()==temp[j+1].getx()){

                }else{
                    if(temp[j].gety()==temp[j-1].gety() && temp[j].gety()==temp[j+1].gety()){

                    }else{
                        temp[j].setCenter(temp[j].getCenter()+delta);
                    }
                }
            }
        }else{
            System.out.println("signal"+net+"can not be processed in right angle");
        }
    }

    public int congestCount(){

        int count = 0;

        for(int i=0; i<2*R-1; i=i+2){
            for(int j=1; j<2*C-1; j=j+2){
                if(w[j][i].getCapacity()>1){
                    count++;
                }
            }
        }

        for(int j=0; j<2*C-1; j=j+2){
            for(int i=1; i<2*R-1; i=i+2){
                if(w[j][i].getCapacity()>1){
                    count++;
                }
            }
        }

        return count;
    }

    /**
     * wireCount method will compute the total wire length of routed nets in unit 1
     * @return
     */
    public int wireCount(){

        int count = 0;
        return count;
    }

    /*
     * viaCount will compute the total number of used vias in routing
     */
    public int viaFaultCount(){

        int count = 0;

        for(int i=0; i<R; i++){
            for(int j=0; j<C; j++){
                if(v[j][i].getCenter()>1){
                    count++;
                }
            }
        }

        return count;
    }
    
    public boolean anneal(int d){

    if (temperature < 1.0E-4) {
      if (d > 0)
        return true;
      else
        return false;
    }

    if (Math.random() < Math.exp(d / temperature))
      return true;
    else
      return false;
    }

    /*
     * method for simulated annealing
     */
    public boolean sa(){

    int cycle = 1;
    int step = 1;
    int sameCount = 0;
    int acceptCount = 0;
    int rejectCount = 0;
    boolean flag; // indicate the routing success or fail

    Map<Integer, List<via>> vroute_copy = new HashMap<Integer, List<via>>();
    Map<Integer, List<wire>> eroute_copy = new HashMap<Integer, List<wire>>();
    Set<Integer> keyset = vroute.keySet();
    Integer[] key = keyset.toArray(new Integer[0]);
    int len = key.length;

    vroute_copy.putAll(vroute);
    eroute_copy.putAll(eroute);

    int present_congest = congestCount() + viaFaultCount();
    int minimum_congest = present_congest;

    while (sameCount < 20) {
      // update the screen
      for (int i = 0; i < len*len; i++){
        int signal = (int)Math.floor((double)len * Math.random());

        ripup(key[signal]);
        reRoute(key[signal]);
        rightAngle(key[signal], 1);
        
        int temp1 = congestCount();
        int temp2 = viaFaultCount();
        System.out.println("The number of congested wires = "+temp1);
        System.out.println("The number of faulted vias = "+temp2);
        int delta = present_congest - temp1 - temp2;

        if (anneal(delta)){
            acceptCount++;
            vroute_copy.putAll(vroute);
            eroute_copy.putAll(eroute);
            present_congest = temp1 + temp2;
        }else{
            rejectCount++;
            vroute.putAll(vroute_copy);
            eroute.putAll(eroute_copy);
        }

        step++;
      }

      // See if this improved anything
      int final_congest = present_congest;
      if (final_congest < minimum_congest){
          minimum_congest = final_congest;
          sameCount=0;
      }else{
          sameCount++;
          temperature = 0.9 * temperature;
          cycle++;
      }
//      System.out.println("sameCount="+sameCount+" cycle="+cycle);
    }

    // the process of simulated annealing is complete
       
        if(congestCount() + viaFaultCount() == 0){
            System.out.println("Solution found after " + cycle + " cycles.");
            System.out.println("step= "+step+" accept= "+acceptCount+" reject= "+rejectCount);
            System.out.println("\n\nThe final routing is:");
            System.out.println(vroute);
            return true;
        }else{
            System.out.println("Solution can not be found");
            return false;
        }
    }

    public void fileOutput(){

        String[][] buffer = new String[R][C];

        for(int i=0; i<R; i++){
            for(int j=0; j<C; j++){
                buffer[i][j] = v[j][i].getNorth()+""+v[j][i].getEast()+""+v[j][i].getWest()+""
                        +v[j][i].getSouth()+""+v[j][i].getCenter();
            }
        }

        try{
            BufferedWriter out = new BufferedWriter(new FileWriter("output.txt", true));
            out.write("\nTemplate"+Template+"("+Xg+","+Yg+")=[\n");
            for(int i=0; i<R; i++){
                for(int j=0; j<C; j++){
                    out.write(buffer[i][j]+" ");
                }
                out.write("\n");
            }
            out.write("]");
            out.close();
        }catch (IOException e){
            
        }
    }

    //detail router main method
    public void detailRoute() throws IOException{
        // TODO code application logic here

        graphConstruct();
        netlistConstruct();
        initialRoute();
        weightClear();
        congestRoute();
        
        if(sa()){
            viaConfigure();
            fileOutput();
        }else{
            System.out.println("Redo global routing");
        }


        //-----------------------following code is for visualization------------

        Layout<via, wire> layout = new XYLayoutBox(getBox());

        layout.setSize(new Dimension(C*100, R*100));
        BasicVisualizationServer<via, wire> vs = new BasicVisualizationServer
                <via, wire>(layout);
        vs.setPreferredSize(new Dimension(C*100+50, R*100+50));

        Transformer<via, Paint> vertex2paint = new Transformer<via, Paint>() {

            public Paint transform(via i) {
               switch(i.getColor())
               {
                   case RED: return Color.RED;
                   case GREEN: return Color.GREEN;
                   case BLUE: return Color.BLUE;
                   case YELLOW: return Color.YELLOW;
                   default: return Color.BLACK;
               }
            }
        };

        float dash[] = {10.0f};
        final Stroke edgeStroke =
                new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER,
                10.0f, dash, 0.0f);
        Transformer<wire, Stroke> edge2stroke = new Transformer<wire, Stroke>() {

            public Stroke transform(wire i) {
                return edgeStroke;
            }
        };

        Transformer<via, String> vertex2string = new Transformer<via, String>() {

            public String transform(via i) {
                return "N"+i.getNorth()+"E"+i.getEast()+"W"+i.getWest()+"S"+i.getSouth()+"C"+i.getCenter();
//                return i.getid()+"("+i.getx()+","+i.gety()+")";
//                return i.getWeight()+","+i.getWeightWave();
            }
        };

        Transformer<wire, String> edge2string = new Transformer<wire, String>() {

            public String transform(wire i) {
//                return i.getid()+"("+i.getx()+","+i.gety()+")";
//                return "";
                return i.getCapacity()+"";
            }
        };

          vs.getRenderContext().setVertexFillPaintTransformer(vertex2paint);
          vs.getRenderContext().setVertexLabelTransformer(vertex2string);
          vs.getRenderer().getVertexLabelRenderer().setPosition(Position.CNTR);
          vs.getRenderContext().setEdgeLabelTransformer(edge2string);

        JFrame frame = new JFrame("FPGA Detail Model at x="+Xg+" y="+Yg);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(vs);
        frame.pack();
        frame.setVisible(true);
    }

}
